#![allow(unused_variables)]
#![allow(dead_code)]

pub mod gdt {
    /// GDT表项
    ///
    /// 注意：在x86_64中段基址与段界限被忽略，可以填写为`0x0`与`0xFFFF_FFFF`
    #[repr(packed)]
    #[derive(Copy, Clone)]
    pub struct GDTEntry {
        /// 段界限（低）
        limit_low: u16,
        /// 段基址（低）
        base_low: u16,
        /// 段基址（中）
        base_mid: u8,
        /// 属性，与段界限（高）
        attr: u16,
        /// 段基址（高）
        base_high: u8,
    }

    impl GDTEntry {
        /// 使用段基址、段界限和段属性创建一个GDTEntry
        fn new(base: u32, limit: u32, attr: u16) -> GDTEntry {
            GDTEntry {
                limit_low: limit as u16,
                base_low: base as u16,
                base_mid: (base >> 16) as u8,
                attr: (((limit >> 8) & 0x0f00) as u16 | (attr & 0xf0ff)),
                base_high: (base >> 24) as u8,
            }
        }
    }
}

pub mod idt {
    /// IDT表项（x86_64）
    ///
    /// 注意：IDT在x86与x86_64中有不同的结构
    #[repr(u128)]
    #[derive(Copy, Clone)]
    pub struct IDTEntry<F> {
        /// 指针偏移（低）
        offset_low: u16,
        /// 段选择子
        segment_selector: u16,
        /// 门属性
        attr: u16,
        /// 指针偏移（中）
        offset_mid: u16,
        /// 指针偏移（高）
        offset_high: u32,
        /// 保留
        reserved: u32,
        phantom: PhantomData<F>,
    }

    impl<F> IDTEntry<F> {
        /// 从段选择子、偏移与属性创建一个IDT表项
        pub fn new(segment_selector: u16, offset: u64, attr: u16) -> IDTEntry<F> {
            IDTEntry {
                segment_selector,
                offset_low: offset as u16,
                offset_mid: (offset >> 16) as u16,
                offset_high: (offset >> 32) as u32,
                attr,
                reserved: 0,
                phantom: PhantomData,
            }
        }

        /// 门属性：存在
        ///
        /// 表明该门（指向的内容）存在于内存中
        pub const PRESENT: u16 = 0x8000;
        /// 门属性：特权级0
        pub const DPL_0: u16 = 0x0000;
        /// 门属性：中断门
        ///
        /// 表明该门是一个中断门。
        /// CPU会清除`RFLAGS.IF`。
        pub const TYPE_INTERRUPT_GATE: u16 = 0x0e00;
        /// 门属性：陷阱门
        ///
        /// 表明该门是一个陷阱门。
        /// CPU不会清除`RFLAGS.IF`。
        pub const _TYPE_TRAP_GATE: u16 = 0x0f00;
    }

    impl<F> Default for IDTEntry<F> {
        /// `IDTEntry`的默认值。
        ///
        /// 创建一个不存在的中断门。
        fn default() -> IDTEntry<F> {
            IDTEntry::new(0, 0, IDTEntry::<F>::TYPE_INTERRUPT_GATE)
        }
    }

    impl<F> From<(F, u16)> for IDTEntry<F> {
        fn from(func: (F, u16)) -> IDTEntry<F> {
            let (func, seg) = func;
            IDTEntry::<F>::new(
                seg,
                unsafe { *(&func as *const F as *const *const u64) as u64 },
                IDTEntry::<F>::PRESENT | IDTEntry::<F>::DPL_0 | IDTEntry::<F>::TYPE_INTERRUPT_GATE,
            )
        }
    }

    #[repr(packed)]
    pub struct InterruptStackFrame {
        pub instruction_pointer: u64,
        pub code_segment: u64,
        pub cpu_flags: u64,
        pub stack_pointer: u64,
        pub stack_segment: u64,
    }

    pub type InterruptFunc = unsafe extern "x86-interrupt" fn(InterruptStackFrame);
    pub type InterruptFuncWithErrorCode =
        unsafe extern "x86-interrupt" fn(InterruptStackFrame, error_code: u64);

    #[repr(packed)]
    pub struct IDT {
        /// #DE 0x0
        pub divide_by_zero: IDTEntry<InterruptFunc>,
        /// #DB 0x1
        pub debug: IDTEntry<InterruptFunc>,
        /// NMI 0x2
        pub nmi: IDTEntry<InterruptFunc>,
        /// #BP 0x3
        pub break_point: IDTEntry<InterruptFunc>,
        /// #OF 0x4
        pub overflow: IDTEntry<InterruptFunc>,
        /// #BR 0x5
        pub bound_range_exceeded: IDTEntry<InterruptFunc>,
        /// #UD 0x6
        pub invalid_opcode: IDTEntry<InterruptFunc>,
        /// #NM 0x7
        pub device_not_available: IDTEntry<InterruptFunc>,
        /// #DF 0x8
        pub double_fault: IDTEntry<InterruptFuncWithErrorCode>,
        /// 0x9
        pub coprocessor_segment_overrun: IDTEntry<InterruptFunc>,
        /// #TS 0xa
        pub invalid_tss: IDTEntry<InterruptFuncWithErrorCode>,
        /// #NP 0xb
        pub segment_not_present: IDTEntry<InterruptFuncWithErrorCode>,
        /// #SS 0xc
        pub stack_segment_fault: IDTEntry<InterruptFuncWithErrorCode>,
        /// #GP 0xd
        pub general_protection_fault: IDTEntry<InterruptFuncWithErrorCode>,
        /// #PF 0xe
        pub page_fault: IDTEntry<InterruptFuncWithErrorCode>,
        /// Reserved 0xf
        pub reserved1: IDTEntry<InterruptFunc>,
        /// #MF 0x10
        pub x87_fp_exception: IDTEntry<InterruptFunc>,
        /// #AC 0x11
        pub alignment: IDTEntry<InterruptFuncWithErrorCode>,
        /// #MC 0x12
        pub machine_check: IDTEntry<InterruptFunc>,
        /// #XF 0x13
        pub simd_fp_exception: IDTEntry<InterruptFunc>,
        /// #VE 0x14
        pub virtualization_exception: IDTEntry<InterruptFunc>,
        /// Reserved 0x15-0x1d
        pub reserved2: [IDTEntry<InterruptFunc>; 9],
        /// SX 0x1e
        pub security_exception: IDTEntry<InterruptFuncWithErrorCode>,
        /// Reserved 0x1f
        pub reserved3: IDTEntry<InterruptFunc>,
        /// User Defined 0x20-0xff
        pub user_defined: [IDTEntry<InterruptFunc>; 224],
    }

    #[repr(packed)]
    struct IDTPtr {
        limit: u16,
        base: u64,
    }

    impl IDT {
        pub fn empty() -> IDT {
            unsafe { core::mem::transmute([0u8; size_of::<IDT>()]) }
        }

        pub fn load(&'static self) {
            unsafe {
                crate::stdlib::asm::lidtq(&IDTPtr {
                    base: self as *const IDT as u64,
                    limit: (size_of::<IDT>() - 1) as u16,
                } as *const IDTPtr as u64)
            };
        }
    }
}

pub mod paging {
    #[repr(packed)]
    #[derive(Copy, Clone)]
    pub struct PageTableEntry(u64);

    impl PageTableEntry {
        pub fn new_with_child(
            child_pagetable: &[PageTableEntry; 512],
            attr: u64,
        ) -> PageTableEntry {
            assert!(attr & PageTableEntry::PS == 0);
            PageTableEntry((child_pagetable as *const PageTableEntry as usize as u64) | attr)
        }

        pub fn new_with_address(memory_address: u64, attr: u64) -> PageTableEntry {
            assert!(attr & PageTableEntry::PS != 0);
            PageTableEntry(memory_address | attr)
        }

        pub fn new_with_raw(raw: u64) -> PageTableEntry {
            PageTableEntry(raw)
        }

        pub const PRESENT: u64 = 0x1;
        pub const RW: u64 = 0x2;
        pub const US: u64 = 0x4;
        pub const PWT: u64 = 0x8;
        pub const PCD: u64 = 0x10;
        pub const ACCESSES: u64 = 0x20;
        pub const DIRTY: u64 = 0x40;
        pub const PS: u64 = 0x80;
        pub const GLOBAL: u64 = 0x100;
        pub const NX: u64 = 0x8000_0000_0000_0000;
    }

    pub struct MemoryBlock {
        start_adderss: u64,
        end_address: u64,
        size: u64,
    }

    impl MemoryBlock {
        pub fn new_with_range(range: Range<u64>) -> MemoryBlock {
            MemoryBlock {
                start_adderss: range.start,
                end_address: range.end,
                size: range.end - range.start,
            }
        }

        pub fn new_with_size(start_adderss: u64, size: u64) -> MemoryBlock {
            MemoryBlock {
                start_adderss,
                end_address: start_adderss + size,
                size,
            }
        }
    }
}
